package com.ruicheng.iam.controller;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ruicheng.iam.entity.ProjectInfo;
import com.ruicheng.iam.service.IProjectInfoService;
import com.ruicheng.iam.util.AesUtil;
import com.ruicheng.iam.util.JdbcUtil;
import com.ruicheng.iam.util.RestResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * IAM 认证控制器
 * @author huang guang yue
 * @version v1.0.0
 * @date 2020 2020/11/24 10:25
 */
@RestController
@RequestMapping("/sso/iam/api/v1")
@Slf4j
public class IamController {

    @Resource
    private IProjectInfoService projectInfoService;
    @Resource
    private RestTemplate restTemplate;

    /**
     * 获取 iam 返回的 code
     * @param appCode appCode
     * @param code code
     */
    @GetMapping("/callback/{appCode}")
    public void getCode(@PathVariable String appCode,
                                      @RequestParam String code,
                                      HttpServletResponse response) throws IOException {

        // 项目信息
        List<ProjectInfo> projectInfos = projectInfoService.listByAppCode(appCode);
        if (projectInfos == null || projectInfos.size() < 1){
            log.error("没有找到项目信息，appCode：{}", appCode);
            return;
        }
        if (projectInfos.size() > 2){
            log.error("找到多个项目信息，appCode：{}", appCode);
            return;
        }
        ProjectInfo projectInfo = projectInfos.get(0);

        // token
        String iamToken = this.getIamToken(code, projectInfo);
        // token 获取失败，重定向到登录页
        if(StringUtils.isEmpty(iamToken)){
            log.error("获取 iam token 失败，重定向到登录页");
            response.sendRedirect(projectInfo.getLoginUrl());
            return;
        }

        // iam 返回的登陆名
        RestResult<String> result = this.getUsername(toString(), projectInfo);
        if (result.getCode() == 0) {
            // 获取用户信息 FIXME 1，根据jdbc获取项目数据库的用户信息；2，生成 token；3，带上 token 跳转 sso 免登录地址；4，解析 token；5，token 5分钟时效性设计
            Statement statement = JdbcUtil.getStatement(projectInfo.getDbHost(), projectInfo.getDbPort(), projectInfo.getDbUsername(), projectInfo.getDbPassword());
            String username = result.getData();
            Assert.notNull(statement, "statement 未空");
            String password = JdbcUtil.getPasswordByUsername(statement, username, projectInfo.getDbUsername(), projectInfo.getTableName(), projectInfo.getLoginNameFiled(), projectInfo.getPasswordFiled());
            // 生成 token
            String ssoToken = AesUtil.encode(username + "-_" + password + "-_" + System.currentTimeMillis(), "airr");
            // 跳转 sso 登录页
            response.sendRedirect(projectInfo.getSsoLoginUrl() + "?ssoToken=" + ssoToken);
        } else {
            // 登录名获取失败, 跳转登录页
            response.sendRedirect(projectInfo.getLoginUrl());
        }

    }

    /**
     * 解析 ssoToken
     * @param ssoToken ssoToken
     * @return com.ruicheng.iam.util.RestResult
     */
    @GetMapping("/decode/{ssoToken}")
    public RestResult<Map<String, String>> decodeToken(@PathVariable String ssoToken){
        if (StringUtils.isEmpty(ssoToken)){
            return RestResult.error("ssoToken 为空");
        }
        String str = AesUtil.decode(ssoToken, "airr");
        if (!str.contains("-_")){
            return RestResult.error("ssoToken 不合法");
        }
        String[] split = str.split("-_");
        if (split.length < 3){
            return RestResult.error("ssoToken 不合法");
        }
        String timeMillis = split[2];
        double l = (System.currentTimeMillis() - Long.parseLong(timeMillis)) / 1000.00 / 60.00;
        if (l < 5.00){
            return RestResult.error("ssoToken 过期");
        }
        return RestResult.success(new HashMap<String, String>(2) {{
            put("username", split[0]);
            put("password", split[1]);
        }});

    }

    /**
     * 通过 code 获取 token
     * @param code code
     * @param projectInfo 项目信息
     * @return java.lang.String
     */
    private String getIamToken(String code, ProjectInfo projectInfo){
        // iam 生产环境获取 token
        String url = "http://iam.xnyqt.petrochina/ngiam-rst/oauth2/token" +
                "?appcode=" + projectInfo.getAppCode() +
                "&secret=" + projectInfo.getAppSecret() +
                "&code=" + code;
        String rep = null;
        try {
            rep = restTemplate.getForObject(url, String.class);
        } catch (RestClientException e) {
            log.error("获取 iam token 失败，url：{}", url);
        }
        if (rep == null){
            return null;
        }
        JSONObject jsonObject = JSONObject.parseObject(rep);
        return jsonObject.getString("accessToken");
    }

    /**
     * 获取项目登陆用户名
     * @param token token
     * @return java.util.Map<java.lang.String, java.lang.Object>
     */
    private RestResult<String> getUsername(String token, ProjectInfo projectInfo){

        String url = "http://iam.xnyqt.petrochina/ngiam-rst/oauth2/userinfo" +
                "?appcode=" + projectInfo.getAppCode() +
                "&secret=" + projectInfo.getAppSecret() +
                "&token=" + token;

        String rep;
        try {
            rep = restTemplate.getForObject(url, String.class);
        } catch (RestClientException e) {
            log.error("获取 iam 用户信息失败，url:{}", url);
            return RestResult.error(-1, "获取用户信息失败");
        }
        JSONObject repJsonObject = JSONObject.parseObject(rep);
        // 当前用户系统访问权限列表
        JSONArray appCodes = repJsonObject.getJSONArray("appCodes");
        // 是否有项目访问权限
        boolean auth = appCodes.contains(projectInfo.getAppCode());
        if (!auth) {
            return RestResult.error(-2, "当前用户没有项目访问权限");
        }

        // 获取项目登陆用户名
        String accountName = repJsonObject.getString("accountName");
        if (StringUtils.isEmpty(accountName)){
            return RestResult.error(-3, "iam 没有返回项目登陆用户名");
        }
        return RestResult.success("获取登陆用户成功", accountName);
    }

}
